package com.dayuanit.dy16.ebook.ebookweb.service.impl;

import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.BizAbilityData;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.alipay.api.request.AlipayTradeQueryRequest;
import com.alipay.api.response.AlipayTradeQueryResponse;
import com.dayuanit.dy16.ebook.ebookweb.domain.OrderDomain;
import com.dayuanit.dy16.ebook.ebookweb.dto.AddressDTO;
import com.dayuanit.dy16.ebook.ebookweb.dto.CartDTO;
import com.dayuanit.dy16.ebook.ebookweb.dto.OrderDTO;
import com.dayuanit.dy16.ebook.ebookweb.entity.*;
import com.dayuanit.dy16.ebook.ebookweb.enums.OrderStatusEnum;
import com.dayuanit.dy16.ebook.ebookweb.exception.BizException;
import com.dayuanit.dy16.ebook.ebookweb.mapper.*;
import com.dayuanit.dy16.ebook.ebookweb.service.IOrderService;
import com.dayuanit.dy16.ebook.ebookweb.service.IRedisService;
import com.dayuanit.dy16.ebook.ebookweb.util.DateUtils;
import com.dayuanit.dy16.ebook.ebookweb.util.MoneyUtils;
import com.dayuanit.dy16.ebook.ebookweb.vo.AddressRequestVO;
import com.dayuanit.dy16.ebook.ebookweb.vo.SettlementRequestVO;
import com.dayuanit.dy16.pay.alipayspringbootautoconfiguation.config.AlipayOrderResult;
import com.dayuanit.dy16.pay.alipayspringbootautoconfiguation.handler.AlipayHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class OrderServiceImpl extends BaseService implements IOrderService {

    @Autowired
    private AddressMapper addressMapper;

    @Autowired
    private IRedisService redisService;

    @Autowired
    private AreaMapper areaMapper;

    @Autowired
    private CartMapper cartMapper;

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private OrderDetailMapper orderDetailMapper;

    @Autowired
    private CommodityMapper commodityMapper;

    @Autowired
    private AlipayHandler alipayHandler;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Override
    public List<AddressDTO> loadAddress(long userId) {
        List<Address> addresses = addressMapper.listOrderByUserId(userId);
        List<AddressDTO> dtos = new ArrayList<>(addresses.size());

        for (Address address : addresses) {
            AddressDTO addressDTO = new AddressDTO();
            addressDTO.setAddressId(address.getId());
            addressDTO.setArea(address.getArea());
            addressDTO.setAreaValue(redisService.getAreaValue(address.getArea()));
            addressDTO.setCity(address.getCity());
            addressDTO.setCityValue(redisService.getAreaValue(address.getCity()));
            addressDTO.setDefaultAddr(address.getDefaultAddr());
            addressDTO.setDetail(address.getDetail());
            addressDTO.setPhone(address.getPhone());
            addressDTO.setProvince(address.getProvince());
            addressDTO.setProvinceValue(redisService.getAreaValue(address.getProvince()));
            addressDTO.setRealName(address.getRealName());

            dtos.add(addressDTO);
        }
        return dtos;
    }

    @Override
    public List<Area> loadArea(int level, int parentId) {
        List<Area> areas = areaMapper.listArea(level, parentId);
        return areas;
    }

    @Override
    public void saveAddress(AddressRequestVO vo, long userId) {
        Address address = new Address();
        address.setArea(vo.getArea());
        address.setCity(vo.getCity());
        address.setCreateTime(new Date());
        address.setDefaultAddr(vo.getDefaultAddr());
        address.setDetail(vo.getDetail());
        address.setPhone(vo.getPhone());
        address.setProvince(vo.getProvince());
        address.setRealName(vo.getRealName());
        address.setStatus(1);
        address.setUserId(userId);
        addressMapper.insert(address);
    }

    @Override
    public List<CartDTO> loadCartInfo(long userId) {
        List<SettlementRequestVO> settleInfo = redisService.getSettleInfo(userId);
        List<CartDTO> dtos = new ArrayList<>(settleInfo.size());

        List<Long> cartIds = settleInfo.stream().map(e -> e.getCartId()).collect(Collectors.toList());

        List<Cart> carts = cartMapper.listByIds(cartIds);

        return formatCartDTO(carts);
    }

    @Transactional
    @Override
    public String createOrder(long userId, long addressId, int payChannel) {

        List<SettlementRequestVO> settleInfo = redisService.getSettleInfo(userId);

        List<Long> commodityIds = settleInfo.stream().map(e -> e.getCommodityId()).collect(Collectors.toList());

        Order order = new Order();

        Address address = addressMapper.selectByPrimaryKey(addressId);
        order.setArea(address.getArea());
        order.setCity(address.getCity());
        order.setCreateTime(new Date());
        order.setDetailAddr(address.getDetail());
        order.setModityTime(order.getCreateTime());
        order.setProvince(address.getProvince());
        order.setStatus(1);
        //TODO 重新计算
        order.setTotalAmount(10);
        order.setUserId(userId);
        order.setPayChannel(payChannel);

        //添加主订单
        orderMapper.insert(order);

        // 订单明细表
        Map<Long, SettlementRequestVO> settmentMap = settleInfo.stream().collect(Collectors.toMap(e -> e.getCommodityId(), e -> e));
        for (Long commodityId : commodityIds) {
            Commodity commodity = commodityMapper.getById4Lock(commodityId);

            SettlementRequestVO vo = settmentMap.get(commodity.getId());

            OrderDetail orderDetail = new OrderDetail();

            orderDetail.setBuyNum(vo.getBuyNum());
            orderDetail.setCommoId(commodity.getId());
            orderDetail.setCommoName(commodity.getBookName());
            orderDetail.setOrderId(order.getId());
            orderDetail.setPrice(commodity.getPrice());
            orderDetailMapper.insert(orderDetail);

            // 库存扣减
            if (commodity.getStock() < orderDetail.getBuyNum()) {
                throw new BizException("库存不足");
            }

            int rows = commodityMapper.updateStock(commodity.getId(), orderDetail.getBuyNum(), commodity.getStock());
            if (1 != rows) {
                throw new BizException("库存扣减失败");
            }
        }

        // 向消息队列发送 订单消息
        rabbitTemplate.convertAndSend("deadDirectExchange", "delayRoutingKey", order.getId());

        // 生成支付宝收银台地址
        String payRequestForm = alipayHandler.createPayRequestForm(order.getId().toString());
        return payRequestForm;
    }

    @Transactional
    @Override
    public void alipayNotify(Map<String, String> notifyParamter) {
        // 请求的来源（通过非对称加密RSA搞定 也就是验签名）
        //将异步通知中收到的所有参数都存放到map中
        //验证订单是否存在
        AlipayOrderResult alipayOrderResult = alipayHandler.processNotify(notifyParamter);
        if (!alipayOrderResult.isSuccess()) {
            throw new BizException(alipayOrderResult.getMessage());
        }

        Order order = orderMapper.selectByPrimaryKey(Long.valueOf(alipayOrderResult.getOrderId()));
        if (null == order) {
            throw new BizException("订单不存在");
        }

        //幂等性
        if (order.getStatus() != OrderStatusEnum.wait_pay.getK()) {
            throw new BizException("订单无法处理");
        }

        //校验支付金额
        if (MoneyUtils.yuan2Fen(alipayOrderResult.getAmount()) != order.getTotalAmount()) {
            throw new BizException("金额有误");
        }

        // 改变订单的状态 并发(锁)  乐观锁
        int rows = orderMapper.updateStatus(order.getId(), order.getStatus(), OrderStatusEnum.pay_success.getK());
        if (1 != rows) {
            throw new BizException("订单处理失败");
        }
    }

    @Override
    @Transactional
    @RabbitListener(queues = "realyQueue")
    public void processCancelOrder(Long orderId) {

        System.out.println("rabbitMQ过期的订单：" + orderId);
        Order order = orderMapper.selectByPrimaryKey(orderId);

        int rows = orderMapper.updateStatus(order.getId(), order.getStatus(), OrderStatusEnum.cancel.getK());
        if (1 != rows) {
            throw new BizException("订单"+order.getId()+"取消失败");
        }

        // 释放库存
        List<OrderDetail> orderDetails = orderDetailMapper.listByOrderId(order.getId());
        for (OrderDetail orderDetail : orderDetails) {
            Commodity commodity = commodityMapper.getById4Lock(orderDetail.getCommoId());
            rows = commodityMapper.updateStock(commodity.getId(), -orderDetail.getBuyNum(), commodity.getStock());
            if (1 != rows) {
                throw new BizException("订单"+order.getId()+"取消失败");
            }
        }
    }

    @Transactional
    @Override
    public void queryAliOrder(Order order) {
        AlipayClient alipayClient =  new DefaultAlipayClient( "http://payhub.dayuanit.com/gateway/alipay/web.do" ,
                "2020090311524831950",
                "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC5QkiB4HrpFaIuRRWjp7CsJYmFq4G1Op0ywN2iYwBp4uIW423DL/cZvRtjXP3yxPmbC093AdJ8aKMik0NXEFhzKM/8e1UB+iaZFi/j d6nPG/Dkon1y4aiy5z0ok1DNwRRpLYQ51r2A17dQfF9YzU2p9BzaaPMAIbHprvcrcbmwgMM/zFkPB9QAdEInN0OuH/8DG7z26aYknpQGy67WlTV9RXXQR+/vY9pgar81XIK2qPJ6iiTcsYb4XqHAcOzA 8jq1ACmWYk8ZKitjTGlbce/jF6tQzUGuqUCEk4ICKHnOejz8yK5RSuQ1lWqQ2ilqHUsDgeYYtVmZAgf5bfDvpxIlAgMBAAECggEBAKsguU2PiLnqS+el1TlZW2+JNSGOrcA0pqUNVy37eZRWCjbOWsPkeMMD1lrQuZwZq+A2cG8j9cUJ+zejBJ9V7/5ggs9dNccfxau/vPjHvsobq0fneS43mNe5ltr2Sbvm UO5fJzcuT9rGaQUl/gVPkCAf6M8wBtfmSCvufU9aPAb3NfVFbQb0a2d2IHrcEx1JjPyndtA+e2Y+ Aaa3BwbdyV2iEmgOsnzijJe7BfgIVTt51QIIyRl00RUXZdPlgeg48PyQOj9myurYPjAYafKF/GwX WWkDCaTF1n5fEIWuTI8WLWOT9APBKXVjXUc3ar844pvisgqL8QMxp2EhDvOEpTkCgYEA8TF1ccUO bfhlqkTVq+GBOitF/IhNaO90ucPKZ/1zGRvGtH5gC8Ux6QNg7PM83lUAf5HECGJv5EQ4Fs7yGao0 uC7ffJ2/zntI+h/5tUinSMfM8d/MwtuAW7M+nrK7AyZlS5m4Go84esiEV13afLumbNIQKFXNfgWk 5r3QCmdtVZ8CgYEAxKHF7qdhk5WE43kmk5+97NigyN2M35JXndacXxZ7AbMr7BjwnfpSPoUevOyM oxJFfFKySmAZAw1KYbl3oMjm/0qBbzl/15yw0dO/y+cVYz5KPGd8QE7NVgdkz0NJoMk06zBME9B2 K9yRHmLSrB7f3lVqv0lymKwKOhyM8WFzGbsCgYEAmTs2xgTPWgZJcJcyuHQXyYDcgceGhng6zBob aV2X1gfTgbAYDOJHByZiWadesFGIc4OD61++fmLoz3TCUpPT0n/U9DK0PNYOUvolhoqV3KMzDSeF yvXw4lFKI0Chlyq5iAIs+JKs6zSg/3Bc8om0/cWnBjkcyEeFsCI/HZz/1TkCgYA3cusvFpJknOhh gTuIq6ZSUPBeVGOydEEy6F8mTV+Yph7/sDTCR+HDcaSuYAJ0xeVEXsmaQakr/CZ6kmOSurSxTyBa 7hsIAU63jP/sDxkv6FPk3sXiGJa86M7bx7zOk47Rt8fsWn/IdKWTrQh9syMsPmgg0yR2uyUTKBPy yak8mwKBgQCnYsu57EEDumSI4937h7I7gyKtnhMeFcWEfUJmH8Z04zfUVJopyHGFCjJPtZ3k0Y4Z irMsStXJmUThu9+x2mlcsy1siOaWcxaUNSNYMNkx3oWUwD7oNGfYaFd9ig6DvZvEBJMfTNBFj/cB ShPdYeq1WHETSSNigQmhpBZlkGvKvg==",
                "JSON",
                "utf-8",
                "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4SZgs0ZcIyvfS4hv2WEY PtoLpTCmza2IQKsLWxXHWwIFgPsnDB61jvNG9Oij7pmxx/+JMOWkcdVj1ltoRShH Dy3HAelIYxXnpKnHI3x9c6PyYUIX+szZ/dlkCEEbJPQ0tQqvkauK2ue3gDTBeWw/ frSbRUh0sQu47IUX8XO0MoUZDo62/5mnRnq4S+BKltL08eV7Cb5U+d/sIoce/IuR rDe9IKzdRk+efsSGK52nttuMa5NBOXpZrOg4MdLlhepc/g5xuZAIHkn+MAlW/gRD NfKvfOHh49ghpkO8ilNCQEC3KTn6uccl1DfxWcbgm0Slk5d/4XJ0dJWkarcsTcr2 nQIDAQAB",
                "RSA2");  //获得初始化的AlipayClient

        AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
        request.setBizContent("{" +
                "\"out_trade_no\":\""+order.getId()+"\"" +
                "  }");

        try {
            AlipayTradeQueryResponse response = alipayClient.execute(request);
            if(!response.isSuccess()){
                throw new BizException("订单"+order.getId()+"查询失败");
            }

            String tradeStatus = response.getTradeStatus();
            String totalAmount = response.getTotalAmount();
            String outTradeNo = response.getOutTradeNo();

            if (!"TRADE_SUCCESS".equals(tradeStatus) && !"TRADE_FINISHED".equals(tradeStatus)) {
                return;
            }

            if (MoneyUtils.yuan2Fen(totalAmount) != order.getTotalAmount()) {
                throw new BizException("金额有误");
            }

            // 改变订单的状态 并发(锁)  乐观锁
            int rows = orderMapper.updateStatus(order.getId(), order.getStatus(), OrderStatusEnum.pay_success.getK());
            if (1 != rows) {
                throw new BizException("订单查询处理失败");
            }
        } catch (AlipayApiException e) {
            e.printStackTrace();
        }

    }

    /**
     *  n+1的问题  方案一：
     * @param userId
     * @param status
     * @return
     */
    @Override
    public List<OrderDTO> loadOrder(long userId, Integer status) {
        List<Order> orders = orderMapper.loadOrder(userId, status == 0 ? null : status);
        List<OrderDTO> dtos = new ArrayList<>(orders.size());
        for (Order order : orders) {
            OrderDTO orderDTO = new OrderDTO();
            dtos.add(orderDTO);

            orderDTO.setOrderId(order.getId());
            orderDTO.setAddress(order.getDetailAddr());
            orderDTO.setAmount(MoneyUtils.fen2Yuan(order.getTotalAmount()));
            orderDTO.setCreateTime(DateUtils.date2String(order.getCreateTime()));
            orderDTO.setStatus(order.getStatus());
            orderDTO.setStatusValue(OrderStatusEnum.get(order.getStatus()).getV());

            List<OrderDomain.OrderDetailDomain> orderDetailDomains = orderDetailMapper.listByOrderId4Domain(order.getId());
            List<OrderDTO.OrderDetailDTO> detailDtos = new ArrayList<>(orderDetailDomains.size());
            for (OrderDomain.OrderDetailDomain detailDomain : orderDetailDomains) {
                OrderDTO.OrderDetailDTO orderDetailDTO = new OrderDTO.OrderDetailDTO();
                detailDtos.add(orderDetailDTO);

                orderDetailDTO.setBuyNum(detailDomain.getBuyNum());
                orderDetailDTO.setCommodityId(detailDomain.getCommodityId());
                orderDetailDTO.setCommoName(detailDomain.getCommoName());
                orderDetailDTO.setPicUrl(pictureUrl + detailDomain.getPicName());
                orderDetailDTO.setPrice(detailDomain.getPrice());
            }

            orderDTO.setChilds(detailDtos);
        }
        return dtos;
    }

    /**
     * 方案二：
     * 采用mybatis高级映射  collection  解决1:n的问题
     * 但是还是会存在n+1的问题，mybatis解决n+1问题采用延迟加载，意义不大
     * @param userId
     * @param status
     * @return
     */
    @Override
    public List<OrderDTO> loadOrder2(long userId, Integer status) {
        List<OrderDomain> orderDomains = orderMapper.loadOrderAndDetail(userId, status);
        for (OrderDomain orderDomain : orderDomains) {
            System.out.println(orderDomain.getAddress());
        }
        return null;
    }

    /**
     * 方案三：
     * 只需要查询两次就可以了  推荐
     * 方案一和二都有n+1的问题
     * @param userId
     * @param status
     * @return
     */
    @Override
    public List<OrderDTO> loadOrder3(long userId, Integer status) {
        List<Order> orders = orderMapper.loadOrder(userId, status == 0 ? null : status);
        List<Long> orderIds = orders.stream()
                .map(e -> e.getId())
                .collect(Collectors.toList());

        List<OrderDomain.OrderDetailDomain> orderDetailDomains = orderDetailMapper.list4Domain(orderIds);
        Map<Long, List<OrderDTO.OrderDetailDTO>> orderDetailMap = orderDetailDomains.stream()
                .map(e -> {
                    OrderDTO.OrderDetailDTO orderDetailDTO = new OrderDTO.OrderDetailDTO();
                    orderDetailDTO.setPrice(e.getPrice());
                    orderDetailDTO.setPicUrl(pictureUrl + e.getPicName());
                    orderDetailDTO.setCommoName(e.getCommoName());
                    orderDetailDTO.setBuyNum(e.getBuyNum());
                    orderDetailDTO.setCommodityId(e.getCommodityId());
                    orderDetailDTO.setOrderId(e.getOrderId());
                    return orderDetailDTO;
                })
                .collect(Collectors.groupingBy(e -> e.getOrderId()));

        List<OrderDTO> dtos = new ArrayList<>(orders.size());

        //将两次for循环通过Map散列表进行优化 时间复杂度降到O(n)
        for (Order order : orders) {
            OrderDTO orderDTO = new OrderDTO();
            dtos.add(orderDTO);
            orderDTO.setOrderId(order.getId());
            orderDTO.setStatusValue(OrderStatusEnum.get(order.getStatus()).getV());
            orderDTO.setCreateTime(DateUtils.date2String(order.getCreateTime()));
            orderDTO.setAmount(MoneyUtils.fen2Yuan(order.getTotalAmount()));
            orderDTO.setAddress(order.getDetailAddr());
            //Map集合查询时间复杂度O(1)
            orderDTO.setChilds(orderDetailMap.get(order.getId()));
        }

        return dtos;
    }
}
